package com.lxl.zookeeper;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @Author lixilin
 * @Date 2022/1/16 9:57
 * @Description 模拟 客户端监听服务端节点变化,然后做不同的处理,比如只有上线的服务端可以访问已经下线的服务端就不可访问了
 */
public class DistributeClient {

    public static void main(String[] args) throws Exception{
        DistributeClient client = new DistributeClient();
        String path = "/servers";
        // 连接zk集群
        client.getConnect(path);
        // 监听服务端节点变化
        client.watchServer(path);
        // 业务逻辑处理
        client.business();
    }

    private void business() throws InterruptedException {
        Thread.sleep(Long.MAX_VALUE);
    }

    private void watchServer(String path) throws KeeperException, InterruptedException {
        List<String> children = zkClient.getChildren(path, true);
        // 保存在线的服务端主机名
        List<String> hosts = new ArrayList<String>();
        if (children != null) {
            for (String child : children) {
                byte[] hostname = zkClient.getData(path + "/" + child, false, null);
                hosts.add(new String(hostname));
            }
        }
        System.out.println("time : "+new Date()+"所有在线的服务端: ");
        System.out.println(hosts);

    }
    // zk集群主机名称及端口
    private String connectString ="node101:2181,node102:2181,node103:2181";
    // 会话超时时间
    private int sessionTimeout=2000;
    private ZooKeeper zkClient;
    private CountDownLatch connectedSignal = new CountDownLatch(1);
    private void getConnect(final String watchPath) throws Exception {
        /*
        当ZooKeeper实例被创建时,会启动一个线程连接到zookeeper服务,由于对构造函数的调用是立即返回的,因此在
        使用新建的zkClient对象前一定要等待其与ZooKeeper服务之间建立连接.
         */
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                // 监听器处理逻辑
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    // 连接成功,释放锁
                    connectedSignal.countDown();
                    try {
                        watchServer(watchPath);
                    } catch (KeeperException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // 等待,直到连接成功才返回
        connectedSignal.await();
    }
}
